/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * MainFrame.java
 *
 * Created on Dec 6, 2010, 11:25:05 AM
 */
package za.co.pass.jtorrenteditor.view;

import java.awt.Color;
import java.awt.Cursor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import za.co.pas.jtorrentparser.parser.FileFormatException;
import za.co.pas.jtorrentparser.parser.IllegalCharacterException;
import za.co.pass.jtorrenteditor.list.model.PiecesListModel;
import za.co.pass.jtorrenteditor.list.model.FileListModel;
import za.co.pass.jtorrenteditor.list.model.AnnounceListModel;
import za.co.pass.jtorrenteditor.helper.Utils;
import za.co.pas.jtorrentparser.parser.TorrentParser;
import za.co.pas.jtorrenttracker.TrackerConnector;
import za.co.pass.jtorrenteditor.helper.CheckThread;
import za.co.pass.jtorrenteditor.list.cellrenderer.FixedTextWidthListCellRenderer;
import za.co.pass.jtorrenteditor.list.model.data.AnnounceData;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.SwingUtilities;

/**
 *
 * @author alabuschagne
 */
public class MainFrame extends javax.swing.JFrame
{

    private static final String TITLE = "jTorrent Editor";
    private Map<String, Object> map = null;
    private String inputFile;
    private String outputFile;
    private String currentInDirectory;
    private String currentOutDirectory;
    private AnnounceListModel announceListModel = new AnnounceListModel();
    private PiecesListModel piecesListModel = new PiecesListModel();
    private FixedTextWidthListCellRenderer piecesCellRenderer = new FixedTextWidthListCellRenderer(true);
    private FixedTextWidthListCellRenderer announceCellRenderer = new FixedTextWidthListCellRenderer();
    private FixedTextWidthListCellRenderer filesCellRenderer = new FixedTextWidthListCellRenderer();
    private CheckThread[] threads = null;
    private AnnounceData announceData = null;
    private Timer timer = null;
    private TimerTask timerTask = new TimerTask()
    {

        @Override
        public void run()
        {
            SwingUtilities.invokeLater(new Runnable()
            {

                public void run()
                {
                    if (announceData != null)
                    {
                        if ((!announceData.isChecked()) && (!announceData.isBusy()) && (!announceData.isOk()))
                        {
                            announce.setBackground(new Color(0xFFFFFF));
                        }
                        else if ((!announceData.isChecked()) && (announceData.isBusy()) && (!announceData.isOk()))
                        {
                            announce.setBackground(new Color(0xFFFF00));
                        }
                        else if ((announceData.isChecked()) && (!announceData.isBusy()) && (!announceData.isOk()))
                        {
                            announce.setBackground(new Color(0xFF0000));
                        }
                        else if ((announceData.isChecked()) && (!announceData.isBusy()) && (announceData.isOk()))
                        {
                            announce.setBackground(new Color(0x00FF00));
                        }
                        if(map != null)
                        {
                            StringBuilder str = new StringBuilder("[");
                            str.append(Integer.toString(announceData.getNumberOfSeeds()));
                            str.append("] ");
                            str.append(new String(Utils.short2Char((short[]) map.get("announce"))));
                            announce.setText(str.toString());
                        }
                    }
                    announce_list.updateUI();
                    if (threads == null)
                    {
                        if (timer != null)
                        {
                            timer.cancel();
                            timer = null;
                        }
                    }
                    else
                    {
                        int countBusy = 0;
                        for (int i = 0; i < threads.length; i++)
                        {
                            if (threads[i].isBusy())
                            {
                                countBusy++;
                            }
                        }
                        if (countBusy == 0)
                        {
                            //kill all
                            if (timer != null)
                            {
                                timer.cancel();
                                timer = null;
                            }
                            threads = null;
                            checkButton.setEnabled(true);
                            checkButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Search24.gif")));
                            checkButton.setText("");
                            calcNoPeers();
                        }
                        else
                        {
                            checkButton.setIcon(null);
                            checkButton.setEnabled(false);
                            checkButton.setText(Integer.toString(countBusy) + "/" + Integer.toString(threads.length));
                        }
                    }
                }
            });
        }
    };

    /** Creates new form MainFrame */
    public MainFrame()
    {
        initComponents();
        setTitle(TITLE);
        announce_list.setModel(announceListModel);
        pieces.setModel(piecesListModel);
        pieces.setCellRenderer(piecesCellRenderer);
        announce_list.setCellRenderer(announceListModel);
        files.setCellRenderer(filesCellRenderer);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel3 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jPanel4 = new javax.swing.JPanel();
        jPanel5 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        announce = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        announce_list = new javax.swing.JList();
        addTrackerButton = new javax.swing.JButton();
        delTrackerButton = new javax.swing.JButton();
        swapButton = new javax.swing.JButton();
        upButton = new javax.swing.JButton();
        downButton = new javax.swing.JButton();
        checkButton = new javax.swing.JButton();
        noPeersLabel = new javax.swing.JLabel();
        jLabel8 = new javax.swing.JLabel();
        jLabel9 = new javax.swing.JLabel();
        jLabel10 = new javax.swing.JLabel();
        jLabel11 = new javax.swing.JLabel();
        jPanel6 = new javax.swing.JPanel();
        jLabel3 = new javax.swing.JLabel();
        creationDate = new javax.swing.JTextField();
        jLabel4 = new javax.swing.JLabel();
        comment = new javax.swing.JTextField();
        createdBy = new javax.swing.JTextField();
        jLabel5 = new javax.swing.JLabel();
        jPanel7 = new javax.swing.JPanel();
        jLabel6 = new javax.swing.JLabel();
        pieceLength = new javax.swing.JTextField();
        jScrollPane3 = new javax.swing.JScrollPane();
        pieces = new javax.swing.JList();
        jLabel7 = new javax.swing.JLabel();
        name = new javax.swing.JTextField();
        nameLabel = new javax.swing.JLabel();
        length = new javax.swing.JTextField();
        lengthLabel = new javax.swing.JLabel();
        jScrollPane4 = new javax.swing.JScrollPane();
        files = new javax.swing.JList();
        filesLabel = new javax.swing.JLabel();
        jToolBar1 = new javax.swing.JToolBar();
        openButton = new javax.swing.JButton();
        saveButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true), "Info", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 11), new java.awt.Color(0, 0, 0))); // NOI18N
        jPanel3.setLayout(new java.awt.BorderLayout());

        jPanel5.setBorder(javax.swing.BorderFactory.createTitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true), "Trackers", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 11), new java.awt.Color(0, 0, 0))); // NOI18N

        jLabel1.setText("Tracker:");

        jLabel2.setText("Alt trackers:");

        jScrollPane2.setViewportView(announce_list);

        addTrackerButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/New24.gif"))); // NOI18N
        addTrackerButton.setToolTipText("Add Tracker URL");
        addTrackerButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addTrackerButtonActionPerformed(evt);
            }
        });

        delTrackerButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Delete24.gif"))); // NOI18N
        delTrackerButton.setToolTipText("Remove Tracker URL");
        delTrackerButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                delTrackerButtonActionPerformed(evt);
            }
        });

        swapButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Refresh24.gif"))); // NOI18N
        swapButton.setToolTipText("Swap Main Tracker with Alt. Tracker");
        swapButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                swapButtonActionPerformed(evt);
            }
        });

        upButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Up24.gif"))); // NOI18N
        upButton.setToolTipText("Move Selected Tracker URL Up");
        upButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                upButtonActionPerformed(evt);
            }
        });

        downButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Down24.gif"))); // NOI18N
        downButton.setToolTipText("Move Selected Tracker URL Down");
        downButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                downButtonActionPerformed(evt);
            }
        });

        checkButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Search24.gif"))); // NOI18N
        checkButton.setToolTipText("Check Trackers");
        checkButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                checkButtonActionPerformed(evt);
            }
        });

        noPeersLabel.setText("Zero peers");

        jLabel8.setBackground(new java.awt.Color(255, 255, 255));
        jLabel8.setText("Skipped");
        jLabel8.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));
        jLabel8.setOpaque(true);

        jLabel9.setBackground(new java.awt.Color(255, 255, 0));
        jLabel9.setText("Busy...");
        jLabel9.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));
        jLabel9.setOpaque(true);

        jLabel10.setBackground(new java.awt.Color(0, 255, 0));
        jLabel10.setText("Ok");
        jLabel10.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));
        jLabel10.setOpaque(true);

        jLabel11.setBackground(new java.awt.Color(255, 0, 0));
        jLabel11.setText("Error");
        jLabel11.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));
        jLabel11.setOpaque(true);

        javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
        jPanel5.setLayout(jPanel5Layout);
        jPanel5Layout.setHorizontalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel5Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel5Layout.createSequentialGroup()
                        .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel2)
                            .addComponent(jLabel1)
                            .addComponent(addTrackerButton)
                            .addComponent(delTrackerButton)
                            .addComponent(swapButton)
                            .addComponent(upButton)
                            .addComponent(downButton)
                            .addComponent(checkButton))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 388, Short.MAX_VALUE)
                            .addComponent(announce, javax.swing.GroupLayout.DEFAULT_SIZE, 388, Short.MAX_VALUE)))
                    .addGroup(jPanel5Layout.createSequentialGroup()
                        .addComponent(noPeersLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 270, Short.MAX_VALUE)
                        .addComponent(jLabel8)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel9)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel10)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel11)))
                .addContainerGap())
        );
        jPanel5Layout.setVerticalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel5Layout.createSequentialGroup()
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(announce, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel5Layout.createSequentialGroup()
                        .addComponent(jLabel2)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(addTrackerButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(delTrackerButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(swapButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(upButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(downButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(checkButton))
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 248, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(noPeersLabel)
                    .addComponent(jLabel11)
                    .addComponent(jLabel10)
                    .addComponent(jLabel9)
                    .addComponent(jLabel8))
                .addContainerGap())
        );

        jPanel6.setBorder(javax.swing.BorderFactory.createTitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true), "Torrent file info", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 11), new java.awt.Color(0, 0, 0))); // NOI18N

        jLabel3.setText("Creation date:");

        creationDate.setEditable(false);

        jLabel4.setText("Comment:");

        jLabel5.setText("Create by:");

        javax.swing.GroupLayout jPanel6Layout = new javax.swing.GroupLayout(jPanel6);
        jPanel6.setLayout(jPanel6Layout);
        jPanel6Layout.setHorizontalGroup(
            jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel6Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel3)
                    .addComponent(jLabel4)
                    .addComponent(jLabel5))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(comment, javax.swing.GroupLayout.DEFAULT_SIZE, 378, Short.MAX_VALUE)
                    .addComponent(creationDate, javax.swing.GroupLayout.DEFAULT_SIZE, 378, Short.MAX_VALUE)
                    .addComponent(createdBy, javax.swing.GroupLayout.DEFAULT_SIZE, 378, Short.MAX_VALUE))
                .addContainerGap())
        );
        jPanel6Layout.setVerticalGroup(
            jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel6Layout.createSequentialGroup()
                .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(creationDate, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(comment, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(createdBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel5))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jPanel7.setBorder(javax.swing.BorderFactory.createTitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true), "File information", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 11), new java.awt.Color(0, 0, 0))); // NOI18N

        jLabel6.setText("Piece length:");

        pieceLength.setEditable(false);

        jScrollPane3.setViewportView(pieces);

        jLabel7.setText("Pieces:");

        nameLabel.setText("Name:");

        length.setEditable(false);

        lengthLabel.setText("Length:");

        jScrollPane4.setViewportView(files);

        filesLabel.setText("Files:");

        javax.swing.GroupLayout jPanel7Layout = new javax.swing.GroupLayout(jPanel7);
        jPanel7.setLayout(jPanel7Layout);
        jPanel7Layout.setHorizontalGroup(
            jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel7Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel6)
                    .addComponent(jLabel7)
                    .addComponent(nameLabel)
                    .addComponent(lengthLabel)
                    .addComponent(filesLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
                    .addComponent(pieceLength, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
                    .addComponent(name, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
                    .addComponent(length, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
                    .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE))
                .addContainerGap())
        );
        jPanel7Layout.setVerticalGroup(
            jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel7Layout.createSequentialGroup()
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel6)
                    .addComponent(pieceLength, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel7))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(name, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(nameLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(length, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(lengthLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 222, Short.MAX_VALUE)
                    .addComponent(filesLabel))
                .addContainerGap())
        );

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel4Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel4Layout.createSequentialGroup()
                        .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGap(1, 1, 1))
                    .addComponent(jPanel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jPanel7, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel4Layout.createSequentialGroup()
                .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel4Layout.createSequentialGroup()
                        .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jPanel6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jPanel7, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );

        jScrollPane1.setViewportView(jPanel4);

        jPanel3.add(jScrollPane1, java.awt.BorderLayout.CENTER);

        getContentPane().add(jPanel3, java.awt.BorderLayout.CENTER);

        jToolBar1.setFloatable(false);
        jToolBar1.setRollover(true);

        openButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Open24.gif"))); // NOI18N
        openButton.setToolTipText("Open Torrent File...");
        openButton.setFocusable(false);
        openButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        openButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        openButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                openButtonActionPerformed(evt);
            }
        });
        jToolBar1.add(openButton);

        saveButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/za/co/pass/jtorrenteditor/resources/Save24.gif"))); // NOI18N
        saveButton.setToolTipText("Save Torrent File As...");
        saveButton.setFocusable(false);
        saveButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        saveButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        saveButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveButtonActionPerformed(evt);
            }
        });
        jToolBar1.add(saveButton);

        getContentPane().add(jToolBar1, java.awt.BorderLayout.NORTH);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void openButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_openButtonActionPerformed
    {//GEN-HEADEREND:event_openButtonActionPerformed
        openFile();
    }//GEN-LAST:event_openButtonActionPerformed

    private void saveButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_saveButtonActionPerformed
    {//GEN-HEADEREND:event_saveButtonActionPerformed
        saveFile();
    }//GEN-LAST:event_saveButtonActionPerformed

    private void addTrackerButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_addTrackerButtonActionPerformed
    {//GEN-HEADEREND:event_addTrackerButtonActionPerformed
        String turl = JOptionPane.showInputDialog(this, "Tracker(s) url", JOptionPane.QUESTION_MESSAGE);
        if ((turl != null) && (turl.trim().length() > 0))
        {
            //check to see if it is multiple urls
            if (turl.indexOf(";") != -1)
            {
                int count = 0;
                String[] turls = turl.split(";");
                for (int i = 0; i < turls.length; i++)
                {
                    count += announceListModel.add(turls[i]) ? 1 : 0;
                }
                JOptionPane.showMessageDialog(this, "Added " + Integer.toString(count) + " tracker url(s)", "Tracker url", JOptionPane.INFORMATION_MESSAGE);
            }
            else
            {
                if (announceListModel.add(turl))
                {
                    JOptionPane.showMessageDialog(this, "Tracker url added", "Tracker url", JOptionPane.INFORMATION_MESSAGE);
                }
                else
                {
                    JOptionPane.showMessageDialog(this, "Tracker url not added.\nTracker url already in list.", "Tracker url", JOptionPane.INFORMATION_MESSAGE);
                }
            }
            announce_list.updateUI();
        }
    }//GEN-LAST:event_addTrackerButtonActionPerformed

    private void delTrackerButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_delTrackerButtonActionPerformed
    {//GEN-HEADEREND:event_delTrackerButtonActionPerformed
        int x = announce_list.getSelectedIndex();
        if (x == -1)
        {
            JOptionPane.showMessageDialog(this, "Please select a tracker", "Warning", JOptionPane.WARNING_MESSAGE);
            return;
        }

        try
        {
            announceListModel.del(x);
        }
        catch (IndexOutOfBoundsException ioobe)
        {
            JOptionPane.showMessageDialog(this, "Please select a tracker", "Warning", JOptionPane.WARNING_MESSAGE);
        }
        announce_list.updateUI();
    }//GEN-LAST:event_delTrackerButtonActionPerformed

    private void swapButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_swapButtonActionPerformed
    {//GEN-HEADEREND:event_swapButtonActionPerformed
        int x = announce_list.getSelectedIndex();
        if (x == -1)
        {
            JOptionPane.showMessageDialog(this, "Please select a tracker", "Warning", JOptionPane.WARNING_MESSAGE);
            return;
        }
        String turl = (String) announceListModel.getElementAt(x);
        if (turl != null)
        {
            String cturl = this.announce.getText();
            if (cturl.trim().length() > 0)
            {
                this.announce.setText(turl);
                announceListModel.set(x, cturl);
                announce_list.updateUI();
            }
        }
    }//GEN-LAST:event_swapButtonActionPerformed

    private void upButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_upButtonActionPerformed
    {//GEN-HEADEREND:event_upButtonActionPerformed
        int x = announce_list.getSelectedIndex();
        if (x <= 0)
        {
            return;
        }
        if (announceListModel.up(x))
        {
            x--;
            announce_list.setSelectedIndex(x);
        }
        announce_list.updateUI();
    }//GEN-LAST:event_upButtonActionPerformed

    private void downButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_downButtonActionPerformed
    {//GEN-HEADEREND:event_downButtonActionPerformed
        int x = announce_list.getSelectedIndex();
        if (x <= -1)
        {
            return;
        }
        if (announceListModel.down(x))
        {
            x++;
            announce_list.setSelectedIndex(x);
        }
        announce_list.updateUI();
    }//GEN-LAST:event_downButtonActionPerformed

    private String hEndoced(short[] chars)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chars.length; i++)
        {
            sb.append("%");
            String str = Integer.toHexString(chars[i]);
            if (str.length() < 2)
            {
                sb.append("0");
            }
            sb.append(str);
        }
        return sb.toString();
    }

    private void checkButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_checkButtonActionPerformed
    {//GEN-HEADEREND:event_checkButtonActionPerformed
        if ((announceListModel.getSize() == 0) || (threads != null))
        {
            return;
        }
        short[] info_hash = piecesListModel.getAt(0);
        System.out.println("Info Hash: " + hEndoced(info_hash));
        threads = new CheckThread[1 + announceListModel.getSize()];
        threads[0] = new CheckThread(this, announceListModel, -1, new AnnounceData(new String(Utils.short2Char((short[]) map.get("announce")))), info_hash);

        Thread t = new Thread(threads[0]);
        t.start();

        for (int i = 1; i < threads.length; i++)
        {
            threads[i] = new CheckThread(this, announceListModel, i - 1, announceListModel.getAnnounceData(i - 1), info_hash);
            t = new Thread(threads[i]);
            t.start();
        }

        if (timer != null)
        {
            timer.cancel();
        }
        timer = new Timer();
        timer.scheduleAtFixedRate(timerTask, 0, 1000);
        /*
        TrackerConnector tc = new TrackerConnector();
        tc.setProxy("localhost", 3128, null, null);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < urls.length; i++)
        {
        sb.append(urls[i]);
        sb.append(" ");
        try
        {
        System.out.println("url: " + urls[i]);
        Map<String, Object> tcMap = tc.getTrackerInfo(urls[i], info_hash, 6881, 0, 0, null);
        if (tcMap == null)
        {
        sb.append("[NOT CHECKED]");
        }
        else
        {
        sb.append("[OK]");
        }
        }
        catch (MalformedURLException ex)
        {
        sb.append("[X]");
        sb.append(ex.getMessage());
        }
        catch (IOException ex)
        {
        sb.append("[X]");
        sb.append(ex.getMessage());
        }
        catch (IllegalCharacterException ex)
        {
        sb.append("[X]");
        sb.append(ex.getMessage());
        }
        catch (FileFormatException ex)
        {
        sb.append("[X]");
        sb.append(ex.getMessage());
        }
        sb.append("\n");
        }
        JOptionPane.showMessageDialog(this, sb.toString(), "Tracker status", JOptionPane.INFORMATION_MESSAGE);

         */
    }//GEN-LAST:event_checkButtonActionPerformed

    private void indent(int ind)
    {
        for (int i = 0; i < ind; i++)
        {
            System.out.print("-");
        }
    }

    private void debugMap(Map map, int indent)
    {
        Iterator<String> it = map.keySet().iterator();
        while (it.hasNext())
        {
            String key = it.next();
            indent(indent);
            System.out.println("Key: " + key);
            if (map.get(key) instanceof Map)
            {
                int i = indent + 1;
                debugMap((Map) map.get(key), i);
            }
            else
            {
                indent(indent);
                System.out.println("Value: " + map.get(key).toString());
            }
        }
    }

    private void openFile()
    {
        JFileChooser fc = new JFileChooser();
        if ((currentInDirectory != null) && (currentInDirectory.length() > 0))
        {
            fc.setCurrentDirectory(new File(currentInDirectory));
        }
        fc.setFileFilter(new FileFilter()
        {

            @Override
            public boolean accept(File f)
            {
                if (f.isDirectory())
                {
                    return true;
                }

                String extension = Utils.getExtension(f);
                if (extension != null)
                {
                    if (extension.equals(Utils.torrent))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                return false;
            }

            @Override
            public String getDescription()
            {
                return "*.torrent";
            }
        });

        int res = fc.showOpenDialog(this);
        if (res == JFileChooser.APPROVE_OPTION)
        {
            FileInputStream fis = null;
            try
            {
                this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                File file = fc.getSelectedFile();
                currentInDirectory = fc.getCurrentDirectory().getAbsolutePath();
                fis = new FileInputStream(file);
                byte[] bytes = new byte[(int) file.length()];
                short[] sbytes = new short[bytes.length];
                fis.read(bytes);
                for (int i = 0; i < bytes.length; i++)
                {
                    sbytes[i] = (short) (0x000000FF & ((int) bytes[i]));
                }
                map = TorrentParser.parse(sbytes);
                //Lets load the info
                this.announce.setText(new String(Utils.short2Char((short[]) map.get("announce"))));
                if (map.containsKey("announce-list"))
                {
                    List<List> a_list = (List<List>) map.get("announce-list");
                    //AnnounceListModel sl = new AnnounceListModel();
                    announceListModel.setList(a_list);
                    this.announce_list.updateUI();
                }
                else
                {
                    announceListModel.setList(new LinkedList());
                    this.announce_list.updateUI();
                }

                if (map.containsKey("creation date"))
                {
                    Integer I = (Integer) map.get("creation date");
                    Calendar cal = Calendar.getInstance();
                    cal.setTimeInMillis(I.longValue() * 1000l);
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    this.creationDate.setText(sdf.format(cal.getTime()));
                }

                if (map.containsKey("comment"))
                {
                    this.comment.setText(new String(Utils.short2Char((short[]) map.get("comment"))));
                }

                if (map.containsKey("created by"))
                {
                    this.createdBy.setText(new String(Utils.short2Char((short[]) map.get("created by"))));
                }

                if (map.containsKey("info"))
                {
                    Map<String, Object> mapInfo = (Map<String, Object>) map.get("info");
                    if (mapInfo.containsKey("piece length"))
                    {
                        Integer I = (Integer) mapInfo.get("piece length");
                        this.pieceLength.setText(I.toString() + " b");
                    }
                    if (mapInfo.containsKey("pieces"))
                    {
                        short[] b = (short[]) mapInfo.get("pieces");
                        piecesListModel.set(b);
                        this.pieces.updateUI();
                    }
                    if (mapInfo.containsKey("name"))
                    {
                        this.name.setText(new String(Utils.short2Char((short[]) mapInfo.get("name"))));
                    }
                    if (mapInfo.containsKey("length"))
                    {
                        Integer I = (Integer) mapInfo.get("length");
                        this.length.setText(I.toString() + " b");
                        this.nameLabel.setText("File name:");
                        this.length.setVisible(true);
                        this.lengthLabel.setVisible(true);
                        this.files.setVisible(false);
                        this.filesLabel.setVisible(false);
                    }
                    else
                    {
                        this.nameLabel.setText("Dir. name:");
                        this.length.setVisible(false);
                        this.lengthLabel.setVisible(false);
                        this.files.setVisible(true);
                        this.filesLabel.setVisible(true);
                    }
                    if (mapInfo.containsKey("files"))
                    {
                        List mapFiles = (List) mapInfo.get("files");
                        FileListModel fl = new FileListModel();
                        fl.setFiles(mapFiles);
                        files.setModel(fl);
                    }
                }
                setTitle(TITLE + " - " + file.getAbsolutePath());
                this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
            catch (FileNotFoundException ex)
            {
                this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
            }
            catch (IOException ex)
            {
                this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
            }
            catch (Exception ex)
            {
                this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
            }
            finally
            {
                try
                {
                    fis.close();
                }
                catch (IOException ex)
                {
                    this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }
        }
    }

    private void saveFile()
    {
        //
        String str = this.announce.getText().trim();
        if (str.length() > 0)
        {
            map.put("announce", Utils.byte2Short(str.getBytes()));
        }
        else if (map.containsKey("announce"))//remove it
        {
            map.remove("announce");
        }

        List l = announceListModel.getList();
        if (l.size() > 0)
        {
            map.put("announce-list", l);
        }
        else if (map.containsKey("announce-list"))
        {
            map.remove("announce-list");
        }

        str = this.comment.getText();
        if (str.length() > 0)
        {
            map.put("comment", Utils.byte2Short(str.getBytes()));
        }
        else if (map.containsKey("comment"))//remove it
        {
            map.remove("comment");
        }

        str = this.createdBy.getText();
        if (str.length() > 0)
        {
            map.put("created by", Utils.byte2Short(str.getBytes()));
        }
        else if (map.containsKey("created by"))//remove it
        {
            map.remove("created by");
        }

        str = this.name.getText();//can't delete it
        if (str.length() > 0)
        {
            Map<String, Object> mapInfo = null;
            if (!map.containsKey("info"))
            {
                //then add it
                mapInfo = new HashMap<String, Object>();
                map.put("info", mapInfo);
            }
            else
            {
                mapInfo = (Map<String, Object>) map.get("info");
            }
            mapInfo.put("name", Utils.byte2Short(str.getBytes()));
        }
        else
        {
            JOptionPane.showMessageDialog(this, "File name or Directory required", "Warning", JOptionPane.WARNING_MESSAGE);
            return;
        }

        JFileChooser fc = new JFileChooser();
        if ((currentOutDirectory != null) && (currentOutDirectory.length() > 0))
        {
            fc.setCurrentDirectory(new File(currentOutDirectory));
        }
        else if ((currentInDirectory != null) && (currentInDirectory.length() > 0))
        {
            fc.setCurrentDirectory(new File(currentInDirectory));
        }
        fc.setFileFilter(new FileFilter()
        {

            @Override
            public boolean accept(File f)
            {
                if (f.isDirectory())
                {
                    return true;
                }

                String extension = Utils.getExtension(f);
                if (extension != null)
                {
                    if (extension.equals(Utils.torrent))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }

                return false;
            }

            @Override
            public String getDescription()
            {
                return "*.torrent";
            }
        });

        if (map != null)
        {
            int res = fc.showSaveDialog(this);
            if (res == JFileChooser.APPROVE_OPTION)
            {
                FileOutputStream fos = null;
                try
                {
                    File file = fc.getSelectedFile();
                    if (file.exists())
                    {
                        if (JOptionPane.showConfirmDialog(this, "File exists.\nOverwrite?", "Confirm", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.CANCEL_OPTION)
                        {
                            return;
                        }
                    }
                    this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                    fos = new FileOutputStream(file);
                    TorrentParser.toBytes(map, fos);
                    setTitle(TITLE + " - " + file.getAbsolutePath());
                    this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    JOptionPane.showMessageDialog(this, "File saved", "Info", JOptionPane.INFORMATION_MESSAGE);
                }
                catch (FileNotFoundException ex)
                {
                    this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
                }
                catch (IOException ex)
                {
                    this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
                }
                finally
                {
                    try
                    {
                        fos.close();
                    }
                    catch (IOException ex)
                    {
                        this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                        JOptionPane.showMessageDialog(this, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
                    }
                }
            }
        }
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addTrackerButton;
    private javax.swing.JTextField announce;
    private javax.swing.JList announce_list;
    private javax.swing.JButton checkButton;
    private javax.swing.JTextField comment;
    private javax.swing.JTextField createdBy;
    private javax.swing.JTextField creationDate;
    private javax.swing.JButton delTrackerButton;
    private javax.swing.JButton downButton;
    private javax.swing.JList files;
    private javax.swing.JLabel filesLabel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JPanel jPanel6;
    private javax.swing.JPanel jPanel7;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JTextField length;
    private javax.swing.JLabel lengthLabel;
    private javax.swing.JTextField name;
    private javax.swing.JLabel nameLabel;
    private javax.swing.JLabel noPeersLabel;
    private javax.swing.JButton openButton;
    private javax.swing.JTextField pieceLength;
    private javax.swing.JList pieces;
    private javax.swing.JButton saveButton;
    private javax.swing.JButton swapButton;
    private javax.swing.JButton upButton;
    // End of variables declaration//GEN-END:variables

    public void set(AnnounceData announceData)
    {
        this.announceData = announceData;
    }

    public void calcNoPeers()
    {
        List<String> ips = new LinkedList<String>();
        if(announceData.getPeers() != null)
        {
            for(int i = 0; i < announceData.getPeers().length; i++)
            {
                ips.add(announceData.getPeers()[i]);
            }
        }
        int size = announceListModel.getSize();
        for(int i = 0; i < size; i++)
        {
            String[] peers = announceListModel.getAnnounceData(i).getPeers();
            if(peers != null)
            {
                for(int j = 0; j < peers.length; j++)
                {
                    ListIterator<String> it = ips.listIterator();
                    while(it.hasNext())
                    {
                        String p = it.next();
                        if(p.compareToIgnoreCase(peers[j]) != 0)
                        {
                            it.add(peers[j]);
                        }
                    }
                }
            }
        }
        String pl = "";
        if(ips.size() != 1)
        {
            pl = "s";
        }
        noPeersLabel.setText(Integer.toString(ips.size()) + " peer" + pl);
    }
}
